package xh.server;

import com.alibaba.fastjson.JSON;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import xh.player.PlayerStatusInfo;
import xh.util.Global;
import xh.util.HttpUtils;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * Created by xh on 2016/6/30.
 */
public class PlayerStatusRequestManager {

    long lastUpdateTime = -1;

    private Set<ChannelWrapper> channels = new HashSet<ChannelWrapper>();

    ScheduledExecutorService ses;

    public PlayerStatusRequestManager() {
        ses = Executors.newSingleThreadScheduledExecutor();
        ses.scheduleAtFixedRate(new RemoveInactive(), 5000, 5000, TimeUnit.MILLISECONDS);
    }

    public boolean hasUpdate(long lastUptime) {
        if (lastUpdateTime == -1) {
            return false;
        } else {
            return lastUptime < this.lastUpdateTime;
        }
    }

    public void noticeUpdate(PlayerStatusInfo p) {
        lastUpdateTime = System.currentTimeMillis();

        doResponse(null);
    }

    public void doResponse(Channel channel) {
        final PlayerStatusInfo ps = PlayerController.controller().playerCurrentStatus();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put("ret", 0);
        result.put("updateTime", lastUpdateTime);
        result.put("info", ps);

        if (channel == null) {
            for (ChannelWrapper w : channels) {
                try {
                    w.c.writeAndFlush(HttpUtils.jsonResponse(result)).addListener(ChannelFutureListener.CLOSE);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else {
            channel.writeAndFlush(HttpUtils.jsonResponse(result)).addListener(ChannelFutureListener.CLOSE);
        }
    }

    public synchronized void holdRequest(Channel channel, long createTime, long aliveTime) {
        channels.add(new ChannelWrapper(channel, createTime, aliveTime));
    }

    private class RemoveInactive implements Runnable {
        public void run() {

            synchronized (PlayerStatusRequestManager.this) {

                Iterator<ChannelWrapper> itr = channels.iterator();
                for (; itr.hasNext(); ) {
                    ChannelWrapper c = itr.next();
                    if (!c.c.isActive()) {

                        System.out.println("[RemoveInactive] inactive, remove channel: " + c.c);
                        itr.remove();

                    } else if (System.currentTimeMillis() > c.createTime + c.aliveTime) {
                        itr.remove();

                        Map<String, Object> result = new HashMap<String, Object>(1);
                        result.put("ret", 1);
                        FullHttpResponse noChangeResponse = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(JSON.toJSONString(result).getBytes(Global.DefaultCharset)));
                        noChangeResponse.headers().set(CONTENT_TYPE, "application/json;charset=UTF-8");
                        noChangeResponse.headers().set(CONTENT_LENGTH, noChangeResponse.content().readableBytes());
                        c.c.writeAndFlush(noChangeResponse).addListener(ChannelFutureListener.CLOSE);
                    }
                }
            }
        }
    }

    private class ChannelWrapper {

        Channel c;
        long createTime;
        long aliveTime;

        public ChannelWrapper(Channel c, long createTime, long aliveTime) {
            this.c = c;
            this.createTime = createTime;
            this.aliveTime = aliveTime;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ChannelWrapper that = (ChannelWrapper) o;

            return !(c != null ? !c.equals(that.c) : that.c != null);

        }

        @Override
        public int hashCode() {
            return c != null ? c.hashCode() : 0;
        }
    }


}
